﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Net;
using System.Threading;

namespace RedditCrawler
{
    public partial class MainWindow : Form
    {
        Dictionary<String, RedditEntry> allEntries = new Dictionary<String, RedditEntry>();
        List<RedditEntry> currentEntries = new List<RedditEntry>();
        WebClient webClient = new WebClient();
        const int sleepPeriod = 2000;
        const String databasePath = @"Reddit\Database.txt";
        bool problemEncountered;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void GoToLikedPage()
        {
            textBoxURL.Text = "http://www.reddit.com/user/" + textBoxUsername.Text + "/liked/";
            webBrowserReddit.Url = new Uri(textBoxURL.Text);
            do
            {
                Application.DoEvents();
            } while (webBrowserReddit.ReadyState != WebBrowserReadyState.Complete);
        }

        private void buttonNavigate_Click(object sender, EventArgs e)
        {
            webBrowserReddit.Url = new Uri(textBoxURL.Text);
            do
            {
                Application.DoEvents();
            } while (webBrowserReddit.ReadyState != WebBrowserReadyState.Complete);
        }

        private void textBoxURL_TextChanged(object sender, EventArgs e)
        {

        }

        private void ReportLine(string text)
        {
            listBoxCrawlerEvents.Items.Insert(0, text);
            Application.DoEvents();
            if (listBoxCrawlerEvents.Items.Count > 10000)
            {
                listBoxCrawlerEvents.Items.Clear();
            }
        }

        private void LoadEntriesFromDatabase()
        {
            try
            {
                if (File.Exists(databasePath))
                {
                    String[] lines = File.ReadAllLines(databasePath);
                    foreach (String curLine in lines)
                    {
                        if (!allEntries.ContainsKey(curLine))
                        {
                            allEntries.Add(curLine, new RedditEntry(curLine));
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                
            }
        }

        private void SaveEntriesToDatabase()
        {
            try
            {
                StreamWriter writer = File.CreateText(databasePath);
                foreach (String curLine in allEntries.Keys)
                {
                    writer.WriteLine(curLine);
                }
                writer.Close();
            }
            catch (Exception ex)
            {

            }
        }

        private void ExtractImageList()
        {
            String html = webBrowserReddit.DocumentText;
            List<String> allLinks = Utility.MultiSplit(html, "<a ", "</a>");
            currentEntries = new List<RedditEntry>();

            RedditEntry activeEntry = null;
            foreach (String curLink in allLinks)
            {
                if (curLink.StartsWith("class=\"title "))
                {
                    activeEntry = new RedditEntry();
                    activeEntry.imageURL = Utility.MultiSplit(curLink, "href=\"", "\"")[0];
                    activeEntry.linkText = curLink.Split('>')[1];
                    activeEntry.subreddit = "unknown";

                    //
                    // Special case for imgur: force the URL to be an image url (at this point, we won't know the correct suffix -- we'll have to correct it later)
                    //
                    if (activeEntry.imageURL.StartsWith("http://imgur.com/") && !Utility.IsImageURL(activeEntry.imageURL))
                    {
                        activeEntry.imageURL += ".jpg";
                    }

                    if (Utility.IsImageURL(activeEntry.imageURL))
                    {
                        currentEntries.Add(activeEntry);
                    }
                }
                if (activeEntry != null && curLink.StartsWith("href=\"http://www.reddit.com/r/"))
                {
                    activeEntry.subreddit = Utility.MultiSplit(curLink, "href=\"http://www.reddit.com/r/", "/")[0];
                }
                if (activeEntry != null && curLink.StartsWith("class=\"comments"))
                {
                    activeEntry.commentsURL = Utility.MultiSplit(curLink, "href=\"", "\"")[0];
                }
            }

            listBoxImages.Items.Clear();
            foreach (RedditEntry curEntry in currentEntries)
            {
                listBoxImages.Items.Add(curEntry.subreddit + "|" + curEntry.imageURL + "|" + curEntry.linkText);

                String entryString = curEntry.ToString();
                if (!allEntries.ContainsKey(entryString))
                {
                    allEntries.Add(entryString, curEntry);
                }
            }
        }

        private void DownloadImages()
        {
            foreach (RedditEntry curEntry in currentEntries)
            {
                String directoryPath = @"Reddit\" + curEntry.subreddit + @"\";
                if (!Directory.Exists(directoryPath))
                {
                    Directory.CreateDirectory(directoryPath);
                }

                String fullPath = directoryPath + curEntry.ImageFilename();
                if (File.Exists(fullPath))
                {
                    ReportLine("Already downloaded: " + fullPath);
                }
                else
                {
                    ReportLine("Downloading: " + fullPath);
                    try
                    {
                        webClient.DownloadFile(curEntry.imageURL, fullPath);
                    }
                    catch(Exception ex)
                    {
                        //
                        // Downloads sometimes fail; nothing to do here.
                        //
                        ReportLine("Download failed: " + ex.ToString());
                    }
                    if (problemEncountered)
                    {
                        return;
                    }
                    Application.DoEvents();
                    Thread.Sleep(sleepPeriod);
                }
            }
        }

        private void NextPage()
        {
            String html = webBrowserReddit.DocumentText;
            List<String> allLinks = Utility.MultiSplit(html, "<a ", "</a>");
            
            foreach (String curLink in allLinks)
            {
                if (curLink.Contains("http://www.reddit.com/user/") &&
                    curLink.Contains("/liked/?count=") &&
                    curLink.Contains(" >next"))
                {
                    string newURL = Utility.MultiSplit(curLink, "href=\"", "\"")[0];
                    ReportLine("Next page: " + newURL);

                    webBrowserReddit.Url = new Uri(newURL);
                    do
                    {
                        Application.DoEvents();
                    } while (webBrowserReddit.ReadyState != WebBrowserReadyState.Complete);

                    return;
                }
            }

            problemEncountered = true;
            ReportLine("Failed to load the next page");
        }

        private void buttonExtractImageList_Click(object sender, EventArgs e)
        {
            ExtractImageList();
        }

        private void buttonCrawling_Click(object sender, EventArgs e)
        {
            if (buttonCrawling.Text == "Start Crawling")
            {
                if (textBoxUsername.Text.Length == 0)
                {
                    MessageBox.Show("Please enter a reddit username.");
                    return;
                }

                buttonCrawling.Text = "Stop Crawling";
                problemEncountered = false;
                GoToLikedPage();
                while (!problemEncountered)
                {
                    ExtractImageList();
                    DownloadImages();
                    NextPage();
                }
            }
            else
            {
                problemEncountered = true;
            }
            buttonCrawling.Text = "Start Crawling";
        }

        private void MainWindow_Load(object sender, EventArgs e)
        {

        }

        private void buttonDownloadImages_Click(object sender, EventArgs e)
        {
            DownloadImages();
        }

        private void buttonNextPage_Click(object sender, EventArgs e)
        {
            NextPage();
        }

        private void buttonMakeHTML_Click(object sender, EventArgs e)
        {
            LoadEntriesFromDatabase();
            
            Dictionary<String, List<RedditEntry>> entriesBySubreddit = new Dictionary<String, List<RedditEntry>>();
            foreach (RedditEntry curEntry in allEntries.Values)
            {
                String directoryPath = @"Reddit\" + curEntry.subreddit + @"\";
                if (File.Exists(directoryPath + curEntry.ImageFilename()))
                {
                    if (!entriesBySubreddit.ContainsKey(curEntry.subreddit))
                    {
                        entriesBySubreddit.Add(curEntry.subreddit, new List<RedditEntry>());
                    }
                    entriesBySubreddit[curEntry.subreddit].Add(curEntry);
                }
            }

            String[] imgurTemplateLines;
            String[] galleryTemplateLines;
            try
            {
                imgurTemplateLines = File.ReadAllLines("imgurTemplate.txt");
                galleryTemplateLines = File.ReadAllLines("galleryTemplate.txt");
            }
            catch (Exception ex)
            {
                MessageBox.Show("imgurTemplate.txt and galleryTemplate.txt must be in the same directory as the executable. Perhaps you extracted the execuatable without its supporting files?");
                return;
            }

            Random random = new Random(0);
            foreach (RedditEntry curEntry in allEntries.Values)
            {
                String directoryPath = @"Reddit\" + curEntry.subreddit + @"\";
                String fullHTMLPath = directoryPath + curEntry.HTMLFilename();
                if (File.Exists(directoryPath + curEntry.ImageFilename())) // && !File.Exists(fullHTMLPath)
                {
                    curEntry.SaveHTML(fullHTMLPath, entriesBySubreddit[curEntry.subreddit], imgurTemplateLines, random);
                }
            }

            List<String> subredditOrdering = new List<String>(entriesBySubreddit.Keys);

            subredditOrdering.Sort(delegate(String a, String b) { return entriesBySubreddit[b].Count.CompareTo(entriesBySubreddit[a].Count); });
            foreach(String subredditName in entriesBySubreddit.Keys)
            {
                RedditEntry.SaveGallery(subredditName, entriesBySubreddit[subredditName], entriesBySubreddit, subredditOrdering, galleryTemplateLines);
                if (subredditName == subredditOrdering[0])
                {
                    try
                    {
                        File.Copy("Reddit/" + subredditName + ".html", "Reddit/index.html");
                    }
                    catch (Exception ex)
                    {

                    }
                }
            }
        }

        private void MainWindow_FormClosing(object sender, FormClosingEventArgs e)
        {
            LoadEntriesFromDatabase();
            SaveEntriesToDatabase();
        }

        private void buttonLikedPage_Click(object sender, EventArgs e)
        {
            if (textBoxUsername.Text.Length == 0)
            {
                MessageBox.Show("Please enter a reddit username.");
                return;
            }
            
            GoToLikedPage();
        }
    }
}
